#ifndef ABOS_KERN_MM_H
#define ABOS_KERN_MM_H
#ifndef ABOS_KERNEL
# error "This is a ABOS kernel header; user programs should not #include it"
#endif

#include <include/memlayout.h>
#include <include/assert.h>

// the dirction, used when call the boot_alloc().
#define HEAD 0
#define TAIL 1

/* This macro takes a kernel virtual address -- an address that points above
 * KERNBASE, where the machine's maximum 256MB of physical memory is mapped --
 * and returns the corresponding physical address.  It panics if you pass it a
 * non-kernel virtual address.
 */
#define PADDR(kva)						\
({								\
	uintptr_t __m_kva = (uintptr_t) (kva);		\
	if (__m_kva < KERNBASE)					\
		panic("PADDR called with invalid kva %08lx", __m_kva);\
	__m_kva - KERNBASE;					\
})

/* This macro takes a physical address and returns the corresponding kernel
 * virtual address.  It panics if you pass an invalid physical address. */
#define KADDR(pa)						\
({								\
	uintptr_t __m_pa = (pa);				\
	uint32_t __m_ppn = PPN(__m_pa);				\
	if (__m_ppn >= npage)					\
		panic("KADDR called with invalid pa %08lx", __m_pa);\
	(void*) (__m_pa + KERNBASE);				\
})

/* these macro define some stack operations. */
// return the value under top, then top++.
#define POP(top)	(*(top++))
// top--, then set value the value under top.
#define PUSH(top,value) do {	\
	*(--top)=value;				\
} while (0)
// tell if the stack is empty.
#define EMPTY(top,base)	(top==base)

// global variables..
extern char bootstacktop[], bootstack[];

extern struct Page *pages;
extern size_t npage;
extern struct Page **page_stack_base;
extern struct Page **page_stack_top;

extern uintptr_t kernel_cr3;
extern pde_t *kernel_pgdir;

extern struct Segdesc gdt[];
extern struct Pseudodesc gdt_pd;

// extern functions in mm.c
void mm_init(void);
int	page_alloc(struct Page **pp);
void page_free(struct Page *pp);
int	page_insert(pde_t *pgdir, struct Page *pp, const uintptr_t va, int perm);
void page_remove(pde_t *pgdir, const uintptr_t la);
pte_t *page_pte(pde_t *pgdir, const uintptr_t la, int alloc);


// below is some usefull inline function
// to handle the address translation..
static inline ppn_t
page2ppn(struct Page *pp)
{
	return pp - pages;
}

static inline uintptr_t
page2pa(struct Page *pp)
{
	return page2ppn(pp) << PGSHIFT;
}

static inline struct Page*
pa2page( uintptr_t pa)
{
	if (PPN(pa) >= npage)
		panic("pa2page called with invalid pa");
	return &pages[PPN(pa)];
}

static inline void*
page2kva(struct Page *pp)
{
	return KADDR(page2pa(pp));
}

#endif /* !ABOS_KERN_MM_H */
